#include "CGame.h"
#include "CMainMenuState.h"
#include "CGamePlayState.h"
#include "CUnit.h"
#include "CPlayer.h"
#include "CAnimationManager.h"
#include "CObjectManager.h"
#include "Mail System.h"
#include "Deliverables.h"
#include "CObjGenerator.h"
#include "CLevel.h"
#include "CSpawnpoint.h"
#include "CLevelCollision.h"
#include "CTile.h"
#include "CLevel.h"
#include "CPlayer.h"
#include "SGD Wrappers/CSGD_FModManager.h"
#include "CIntroState.h"
#include <string>
#include <ctime>
using std::string;

#define	 RANDOM_FLOAT(min,max) (((rand()/(float)RAND_MAX)*((max)-(min)))+(min))

CGame::CGame()
{
	m_bIsWindowed = true;
	m_nScreenHeight = 0;
	m_nScreenWidth = 0;
	m_nNumEnemy = 0;
	m_nMusicVol = 1;
	m_nSFXVol = 1;
}
CGame::~CGame()
{

}
CGame* CGame::GetInstance()
{
	static CGame instance;
	return &instance;
}
void CGame::Init(HWND hWnd, HINSTANCE hInstance, int nScreenWidth, int nScreenHeight, bool bIsWindowed)
{

	m_bIsWindowed = bIsWindowed;
	m_nScreenHeight = nScreenHeight;
	m_nScreenWidth = nScreenWidth;

	CSGD_Direct3D::GetInstance()->InitDirect3D(hWnd, m_nScreenWidth, m_nScreenHeight, m_bIsWindowed, true);
	CSGD_TextureManager::GetInstance()->InitTextureManager(CSGD_Direct3D::GetInstance()->GetDirect3DDevice(), CSGD_Direct3D::GetInstance()->GetSprite());

	CSGD_Direct3D::GetInstance()->Clear(0,0,0);
	CSGD_Direct3D::GetInstance()->DeviceBegin();
	CSGD_Direct3D::GetInstance()->SpriteBegin();

	m_bfItem.Init("Ludicrum_Reclamation_Alt.fnt");
	m_bfItem.DrawBitmapText("Loading...", 200, 200, 2.0f);

	CSGD_Direct3D::GetInstance()->SpriteEnd();
	CSGD_Direct3D::GetInstance()->DeviceEnd();
	CSGD_Direct3D::GetInstance()->Present();

	CSGD_DirectSound::GetInstance()->InitDirectSound(hWnd);
	CSGD_WaveManager::GetInstance()->InitWaveManager(hWnd, CSGD_DirectSound::GetInstance()->GetDSObject());
	CSGD_FModManager::GetInstance()->InitFModManager(hWnd);
	CSGD_DirectInput::GetInstance()->InitDirectInput(hWnd, hInstance, DI_KEYBOARD | DI_MOUSE);
	CSGD_DirectInput::GetInstance()->AcquireAll();
	
	CAnimationManager::GetInstance()->Load("Resource/Animations/AlluWalking.bin");
	CAnimationManager::GetInstance()->Load("Resource/Animations/AltWalk.bin");
	CAnimationManager::GetInstance()->Load("Resource/Animations/Fire.bin");
	CAnimationManager::GetInstance()->Load("Resource/Animations/RatWalking.bin");
	CAnimationManager::GetInstance()->Load("Resource/Animations/BoyWalking.bin");
	CAnimationManager::GetInstance()->Load("Resource/Animations/DeadRat.bin");
	CAnimationManager::GetInstance()->Load("Resource/Animations/DeadBoy.bin");


	m_nRatDeath = CSGD_FModManager::GetInstance()->LoadSound("resource/sound/Rat_Squeaks.wav");
	m_nChildDeath = CSGD_FModManager::GetInstance()->LoadSound("resource/sound/Child_Screaming.wav");


	////////////////////////////////////////////////////////////////////////////////////////
	//	Set PreviousTime last so that it is most accurate
	m_dwPreviousTime = GetTickCount();
	//
	////////////////////////////////////////////////////////////////////////////////////////

	m_rectCamera.left = 0;
	m_rectCamera.right = 800;
	m_rectCamera.top = 0;
	m_rectCamera.bottom = 600;
	m_nChoiceSound = CSGD_FModManager::GetInstance()->LoadSound("resource/sound/Ludicrum_Reclamation_Menu_Move.wav");
	m_nChosenSound = CSGD_FModManager::GetInstance()->LoadSound("resource/sound/Ludicrum_Reclamation_Menu_Move_Alt.wav");

	//srand(GetTickCount());
	srand(unsigned int (time(0)));

	ChangeState(CIntroState::GetInstance());
}
void CGame::Shutdown()
{
	CSGD_DirectInput::GetInstance()->UnacquireAll();
	CSGD_DirectInput::GetInstance()->ShutdownDirectInput();
	CSGD_FModManager::GetInstance()->ShutdownFModManager();
	CSGD_WaveManager::GetInstance()->ShutdownWaveManager();
	CSGD_DirectSound::GetInstance()->ShutdownDirectSound();
	CSGD_TextureManager::GetInstance()->ShutdownTextureManager();
	CSGD_Direct3D::GetInstance()->ShutdownDirect3D();
}
bool CGame::Main()
{
	////////////////////////////////////////////////////////////////////////////////////////
	//	Calculate the elapsed time
	//		Start with getting current time
	DWORD dwStartTime = GetTickCount();
	//		Use the start time to calculate elapsed time
	m_fElapsedTime = (float)(dwStartTime - m_dwPreviousTime)/1000.0f;
	//		Remember the time stamp from last frame
	m_dwPreviousTime = dwStartTime;
	//
	////////////////////////////////////////////////////////////////////////////////////////

	////////////////////////////////////////////////////////////////////////////////////////
	//	Collect droplets(of time1?!?!) in a bucket(of time1?!?!)
	m_fGameTime += m_fElapsedTime;
	//
	////////////////////////////////////////////////////////////////////////////////////////

	CSGD_DirectInput::GetInstance()->ReadDevices();

	if (CSGD_DirectInput::GetInstance()->KeyPressed(DIK_RETURN) && (CSGD_DirectInput::GetInstance()->KeyDown(DIK_RALT) || CSGD_DirectInput::GetInstance()->KeyDown(DIK_LALT)))
	{
		if (m_bIsWindowed)
			m_bIsWindowed = false;
		else 
			m_bIsWindowed = true;
		CSGD_Direct3D::GetInstance()->ChangeDisplayParam(GetScreenWidth(), GetScreenHeight(), m_bIsWindowed);
		CSGD_DirectInput::GetInstance()->ReadDevices();
	}
	if(m_vecState.back() == NULL)
		return false;

	if(m_vecState.back()->Input() == false)
		return false;
	if(m_vecState.back() == NULL)
		return false;

	m_vecState.back()->Update(m_fElapsedTime);
	Render();
	return true;
}
void CGame::Render()
{
	CSGD_Direct3D::GetInstance()->Clear(0,0,0);
	CSGD_Direct3D::GetInstance()->DeviceBegin();
	CSGD_Direct3D::GetInstance()->SpriteBegin();

	vector<IGameState*>::iterator StuffIter = m_vecState.begin();

	while(StuffIter != m_vecState.end())
	{
		(*StuffIter)->Render();
		++StuffIter;
	}

	CSGD_Direct3D::GetInstance()->SpriteEnd();
	CSGD_Direct3D::GetInstance()->DeviceEnd();
	CSGD_Direct3D::GetInstance()->Present();
}
void CGame::ChangeState(IGameState *state)
{
	while(m_vecState.size())
	{
		PopState();     
	}
	PushState(state);
}       
void CGame::PopState()
{
	if(!m_vecState.empty())
	{
		m_vecState.back()->Exit();
		m_vecState.pop_back();
	}

}
void CGame::PushState(IGameState *state)
{
	m_vecState.push_back(state);
	if(m_vecState.back() != NULL)
		m_vecState.back()->Enter();
}
void CGame::ClearAllStates()
{
	while (!m_vecState.empty())
	{
		PopState();
	}
}
void CGame::MessageProc(CMessage *message)
{
	switch(message->GetMsgID())
	{
	case SPAWN_UNIT:
		{
			SpawnUnit* msg = (SpawnUnit*)message;
			CUnit* pUnit = new CUnit;

			unsigned int numSpawnpoints = CGamePlayState::GetInstance()->CurrLevel()->Spawnpoints().size();

			float randSpawnPoint = RANDOM_FLOAT(0, numSpawnpoints);

			pUnit->Init((CGamePlayState::GetInstance()->CurrLevel()->Spawnpoints()[(unsigned int)randSpawnPoint]->PosX()), 
				(CGamePlayState::GetInstance()->CurrLevel()->Spawnpoints()[(unsigned int)randSpawnPoint]->PosY()), 
				20.0f, 20.0f, 50, 50, 25, 20, 30, WANDERING, NULL);
			pUnit->SetStartX(pUnit->GetPosX());
			pUnit->SetStartY(pUnit->GetPosY());
			pUnit->SetNextSpotX(RANDOM_FLOAT((pUnit->GetPosX() - pUnit->GetWidth()*2), (pUnit->GetPosX() + pUnit->GetWidth()*2)));
			pUnit->SetNextSpotY(RANDOM_FLOAT((pUnit->GetPosY() - pUnit->GetHeight()*2), (pUnit->GetPosY() + pUnit->GetHeight()*2)));
			pUnit->UnitType(msg->Type());
			pUnit->MaxInterest(100);
			pUnit->CurrInterest(0);
			CGame::GetInstance()->m_nNumEnemy++;
			CObjectManager::GetInstance()->AddObject(pUnit);
			pUnit->Release();
			break;
		}
	case DAMAGE_TARGET:
		{
			DamageMessage* msg = (DamageMessage*)message;
			msg->GetTarget()->Damage(msg->GetDmg());
			break;
		}
	case SPAWN_PUNCHING_BAG:
		{
			SpawnPunchingBag* msg = (SpawnPunchingBag*)message;
			CUnit* pUnit = new CUnit;
			pUnit->SetPosX((float)(rand()%CGame::GetInstance()->GetScreenWidth()));
			pUnit->SetPosY((float)(rand()%CGame::GetInstance()->GetScreenHeight() - 55));
			pUnit->SetVelX((float)(rand()%((50 + 50) - 50)));
			pUnit->SetVelY((float)(rand()%((50 + 50) - 50)));
			pUnit->SetMaxHP(50);
			pUnit->SetCurrHP(50);
			pUnit->SetHeight(20);
			pUnit->SetWidth(20);
			pUnit->MaxInterest(100);
			pUnit->CurrInterest(100);
			CObjectManager::GetInstance()->AddObject(pUnit);
			pUnit->Release();
			break;
		}
	case DESTROY_UNIT:
		{
			DestroyUnitMessage* msg = (DestroyUnitMessage*)message;
			CLevel* level = CGamePlayState::GetInstance()->CurrLevel();
			
			CGamePlayState::GetInstance()->SpawnCount()[4] = CGamePlayState::GetInstance()->SpawnCount()[4] - 1;
			msg->GetUnit()->DeathLevel(level->GetCurrArea());
			msg->GetUnit()->IsDead(true);
			if(((CUnit*)msg->GetUnit())->UnitType() == CHILD)
			{
				CSGD_FModManager::GetInstance()->PlaySound(CGame::GetInstance()->m_nChildDeath);
				CSGD_FModManager::GetInstance()->SetVolume(CGame::GetInstance()->m_nChildDeath, CGame::GetInstance()->SFXVol());
			}
			else
			{
				CSGD_FModManager::GetInstance()->PlaySound(CGame::GetInstance()->m_nRatDeath);
				CSGD_FModManager::GetInstance()->SetVolume(CGame::GetInstance()->m_nRatDeath, CGame::GetInstance()->SFXVol());
			}
			break;
		}
	case DELETE_UNIT:
		{
			DeleteUnitMessage* msg = (DeleteUnitMessage*)message;
			CObjectManager::GetInstance()->RemoveObject(msg->GetUnit());
			break;
		}
	case ADD_LEVEL_COLLISION:
		{
			RECT temp = {0, 0, 0, 0};
			CGamePlayState::GetInstance()->Death(temp);
			AddLevelCollisionMessage* msg = (AddLevelCollisionMessage*)message;
			CLevelCollision* pLevelCollision;
			pLevelCollision = new CLevelCollision(
				msg->Type(), msg->PosX(), msg->PosY(), msg->Width(), msg->Height());
			CObjectManager::GetInstance()->AddObject(pLevelCollision);
			if(msg->Type() == FIRE_TRAP)
			{	
				temp.top = msg->PosY();
				temp.left = msg->PosX();
				temp.bottom = msg->PosY() + msg->Height();
				temp.right = msg->PosX() + msg->Width();
				CGamePlayState::GetInstance()->Death(temp);
			}	
			pLevelCollision->Release();
			break;
		}
	case CREATE_TILE:
		{
			CreateTileMessage* msg = (CreateTileMessage*)message;
			CTile* pTile = msg->Tile();
			(*pTile).TopX((float)msg->PosX());
			(*pTile).TopY((float)msg->PosY());
			break;
		}
	case SPAWN_UNIT_MENU:
		{
			CUnit* pUnit = new CUnit;
			
			float fPosY = RANDOM_FLOAT(25, 575);
			float fPosX = RANDOM_FLOAT(20, 780);

			pUnit->Init(fPosX, fPosY, 20, 20, 50, 50, 25, 20, 30, WANDERING, NULL);
			pUnit->SetStartX(pUnit->GetPosX());
			pUnit->SetStartY(pUnit->GetPosY());
			pUnit->SetNextSpotX(RANDOM_FLOAT((pUnit->GetPosX() - pUnit->GetWidth()*2), (pUnit->GetPosX() + pUnit->GetWidth()*2)));
			pUnit->SetNextSpotY(RANDOM_FLOAT((pUnit->GetPosY() - pUnit->GetHeight()*2), (pUnit->GetPosY() + pUnit->GetHeight()*2)));
			pUnit->UnitType(MENURAT);
			pUnit->MaxInterest(100);
			pUnit->CurrInterest(0);
			CObjectManager::GetInstance()->AddObject(pUnit);
			pUnit->Release();
		}
	}
}